home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
formats
/
iff
/
newiff.lzh
/
NewIFF
/
NewIFF.lzh
/
newiff
/
apps
/
PlaySMUS
/
PlaySMUS.c
< prev
Wrap
C/C++ Source or Header
|
1992-05-18
|
29KB
|
1,029 lines
/** PlaySMUS.c **************************************************************
*
* Read and play a SMUS file
*
* requires linkage with several IFF modules - see Makefile
****************************************************************************/
#include "iffp/8svxapp.h"
#include "iffp/smusapp.h"
#include <exec/execbase.h>
#include <graphics/gfxbase.h>
#include <clib/alib_protos.h>
#ifdef LATTICE
int CXBRK(void) { return(0); } /* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); } /* really */
#endif
/********** debug macros ***********/
#define MYDEBUG 1
void kprintf(UBYTE *fmt,...);
void dprintf(UBYTE *fmt,...);
#define DEBTIME 0
#define bug printf
#if MYDEBUG
#define D(x) (x); if(DEBTIME>0) Delay(DEBTIME);
#else
#define D(x) ;
#endif /* MYDEBUG */
/********** end of debug macros **********/
/* prototypes for our functions */
void cleanup(void);
void bye(UBYTE *s,int error);
void DUnpack(BYTE source[], LONG n, BYTE dest[]);
BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x);
LONG LoadSample(struct EightSVXInfo *esvx, UBYTE *filename);
void UnloadSample(struct EightSVXInfo *esvx);
LONG LoadSBody(struct EightSVXInfo *esvx);
void UnloadSBody(struct EightSVXInfo *esvx);
LONG ShowSample(struct EightSVXInfo *esvx);
LONG LoadSMUS(struct SMUSInfo *smus, UBYTE *filename);
void UnloadSMUS(struct SMUSInfo *esvx);
LONG OpenAudio(void);
void CloseAudio(void);
LONG PlaySample(struct EightSVXInfo *esvx,
LONG octave, LONG note, UWORD volume, ULONG delay);
#define MINARGS 2
char *vers = "$VER: PlaySMUS 37.9";
char *Copyright = "PlaySMUS v37.9 (Freely Redistributable)";
char *usage = "Usage: PlaySMUS SMUSname [instrument path]";
char *inspath = "Instruments:";
/* globals */
struct Library *IFFParseBase = NULL;
struct Library *GfxBase = NULL;
BOOL FromWb;
/* SMUS Property chunks to be grabbed
*/
LONG smusprops[] = {
ID_SMUS, ID_SHDR,
ID_SMUS, ID_NAME,
ID_SMUS, ID_AUTH,
ID_SMUS, ID_Copyright,
TAG_DONE
};
/* SMUS Collection chunks (more than one in file) to be gathered */
LONG smuscollects[] = {
ID_SMUS, ID_ANNO,
TAG_DONE
};
/* SMUS Chunks to stop on */
LONG smusstops[] = {
ID_SMUS, ID_TRAK,
ID_SMUS, ID_INS1,
TAG_DONE
};
/* 8SVX Property chunks to be grabbed
*/
LONG esvxprops[] = {
ID_8SVX, ID_VHDR,
ID_8SVX, ID_NAME,
ID_8SVX, ID_ATAK,
ID_8SVX, ID_RLSE,
ID_8SVX, ID_AUTH,
ID_8SVX, ID_Copyright,
TAG_DONE
};
/* 8SVX Collection chunks (more than one in file) to be gathered */
LONG esvxcollects[] = {
ID_8SVX, ID_ANNO,
TAG_DONE
};
/* 8SVX Chunk to stop on */
LONG esvxstops[] = {
ID_8SVX, ID_BODY,
TAG_DONE
};
UBYTE nomem[] = "Not enough memory\n";
UBYTE noiffh[] = "Can't alloc iff\n";
/* For our allocated SMUSInfo */
struct SMUSInfo *smus = NULL;
/* For allocated 8SVX Infos */
struct EightSVXInfo *esvxs[MAXINS] = { 0 };
struct EightSVXInfo *esvx = NULL;
ULONG ei = 0, ti = 0, tcnt = 0, ecnt = 0;
/*
* MAIN
*/
void main(int argc, char **argv)
{
UBYTE *smusname=NULL, *esvxname=NULL;
ULONG oct;
LONG error=0L;
FromWb = argc ? FALSE : TRUE;
if((argc<MINARGS)||(argv[argc-1][0]=='?'))
{
printf("%s\n%s\n",Copyright,usage);
bye("",RETURN_OK);
}
smusname = argv[1];
if(argc > 2) inspath = argv[2];
/* Open Libraries */
if(!(IFFParseBase = OpenLibrary("iffparse.library",0)))
bye("Can't open iffparse library.\n",RETURN_WARN);
/*
* Alloc one SMUSInfo struct
*/
if(!(smus = (struct SMUSInfo *)
AllocMem(sizeof(struct SMUSInfo),MEMF_PUBLIC|MEMF_CLEAR)))
bye(nomem,RETURN_FAIL);
/*
* Here we set up our SMUSInfo fields for our application.
* Above we have defined the propery and collection chunks
* we are interested in.
* We want to stop on INS1 or TRAK.
*/
smus->ParseInfo.propchks = smusprops;
smus->ParseInfo.collectchks = smuscollects;
smus->ParseInfo.stopchks = smusstops;
/*
* Alloc the IFF handle for the frame
*/
if(!(smus->ParseInfo.iff = AllocIFF())) bye(noiffh,RETURN_FAIL);
/*
* Alloc one EightSVXInfo struct for defaults
*/
if(!(esvx = (struct EightSVXInfo *)
AllocMem(sizeof(struct EightSVXInfo),MEMF_PUBLIC|MEMF_CLEAR)))
bye(nomem,RETURN_FAIL);
/*
* Here we set up default EightSVXInfo fields for our
* application.
* Above we have defined the propery and collection chunks
* we are interested in (some required like VHDR).
* We want to stop on BODY.
*/
esvx->ParseInfo.propchks = esvxprops;
esvx->ParseInfo.collectchks = esvxcollects;
esvx->ParseInfo.stopchks = esvxstops;
/*
* Alloc the IFF handle for the frame
*/
if(!(esvx->ParseInfo.iff = AllocIFF())) bye(noiffh,RETURN_FAIL);
if(error = LoadSMUS(smus, smusname))
{
printf("Error %ld: %s\n",error, IFFerr(error));
goto done;
}
goto done;
if(!(error = LoadSample(esvx, esvxname)))
{
ShowSample(esvx);
if(!(error = OpenAudio()))
{
/* If we think this is a sound effect, play it as such (note=-1) */
if((esvx->Vhdr.ctOctave==1)&&(esvx->Vhdr.samplesPerSec)
&&(esvx->Vhdr.oneShotHiSamples)&&(!esvx->Vhdr.repeatHiSamples))
{
PlaySample(esvx,0,-1,64,0);
}
/* Else play it like an instrument */
else
{
for(oct=0; oct < esvx->Vhdr.ctOctave; oct++)
{
PlaySample(esvx,oct,0,64,50);
PlaySample(esvx,oct,4,64,50);
PlaySample(esvx,oct,7,64,50);
}
}
CloseAudio();
}
else printf("error opening audio device\n");
}
else
printf("%s\n",IFFerr(error));
done:
D(bug("done\n"));
cleanup();
exit(RETURN_OK);
}
void bye(UBYTE *s,int error)
{
if((*s)&&(!FromWb)) printf("%s\n",s);
cleanup();
exit(error);
}
void cleanup()
{
int k;
D(bug("cleanup:\n"));
if(esvx)
{
/*
DD(bug("About to UnloadSample\n"));
UnloadSample(esvx);
*/
if(esvx->ParseInfo.iff) FreeIFF(esvx->ParseInfo.iff);
FreeMem(esvx,sizeof(struct EightSVXInfo));
esvx = NULL;
}
for(k = 0; (k <MAXINS) && (esvxs[k]) ; k++)
{
if(esvxs[k])
{
D(bug("cleanup: about to UnloadSample %ld\n",k));
UnloadSample(esvxs[k]);
D(bug("cleanup: about to FreeIFF %ld\n",k));
if(esvxs[k]->ParseInfo.iff) FreeIFF(esvxs[k]->ParseInfo.iff);
FreeMem(esvxs[k],sizeof(struct EightSVXInfo));
esvxs[k] = NULL;
}
}
if(smus)
{
DD(bug("About to UnloadSMUS\n"));
UnloadSMUS(smus);
if(smus->ParseInfo.iff) FreeIFF(smus->ParseInfo.iff);
FreeMem(smus,sizeof(struct SMUSInfo));
smus = NULL;
}
if(IFFParseBase) CloseLibrary(IFFParseBase);
}
/** ShowSample() **********************************************
*
* Show sample information after calling LoadSample()
*
*************************************************************************/
LONG ShowSample(struct EightSVXInfo *esvx)
{
LONG error = 0L;
BYTE *buf;
Voice8Header *vhdr;
if(!esvx) return(CLIENT_ERROR);
if(!(buf = esvx->sample)) return(CLIENT_ERROR);
/* LoadSample copied VHDR and NAME (if any) to our esvx frame */
vhdr = &esvx->Vhdr;
if(esvx->name[0]) printf("\nNAME: %s",esvx->name);
printf("\n\nVHDR Info:");
printf("\noneShotHiSamples=%ld", vhdr->oneShotHiSamples);
printf("\nrepeatHiSamples=%ld", vhdr->repeatHiSamples);
printf("\nsamplesPerHiCycle=%ld", vhdr->samplesPerHiCycle);
printf("\nsamplesPerSec=%ld", vhdr->samplesPerSec);
printf("\nctOctave=%ld", vhdr->ctOctave);
printf("\nsCompression=%ld", vhdr->sCompression);
printf("\nvolume=0x%lx", vhdr->volume);
printf("\nData = %3ld %3ld %3ld %3ld %3ld %3ld %3ld %3ld",
buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6],buf[7]);
printf("\n %3ld %3ld %3ld %3ld %3ld %3ld %3ld %3ld ...\n",
buf[8+0],buf[8+1],buf[8+2],buf[8+3],buf[8+4],buf[8+5],
buf[8+6],buf[8+ 7]);
return(error);
}
/* OpenAudio
*
* Opens audio device for one audio channel, 2 IO requests
* Returns 0 for success
*
* Based on code by Dan Baker
*/
UBYTE whichannel[] = { 1,2,4,8 };
/* periods for scale starting at 65.40Hz (C) with 128 samples per cycle
* or 130.81Hz (C) with 64 samples per cycle
* or 261.63Hz (C) with 32 samples per cycle
* or 523.25Hz (C) with 16 samples per cycle
* or 1046.50Hz (C) with 8 samples per cycle
* or 2093.00Hz (C) with 4 samples per cycle
*/
UWORD per_ntsc[12]= { 428, 404, 380, 360,
340, 320, 302, 286,
270, 254, 240, 226 };
/* periods adjusted for system clock frequency */
UWORD per[12];
/* Note - these values 3579545 NTSC, 3546895 PAL */
#define NTSC_CLOCK 3579545L
#define PAL_CLOCK 3546895L
#define AIOCNT 4
struct IOAudio *aio[AIOCNT] = {NULL}; /* Ptrs to IO blocks for commands */
struct MsgPort *port; /* Pointer to a port so the device can reply */
BOOL devopened;
ULONG clock = NTSC_CLOCK; /* Will check for PAL and change if necessary */
LONG OpenAudio()
{
extern struct ExecBase *SysBase;
LONG error=0L;
ULONG period;
int k;
if(devopened) return(-1);
/*-------------------------------------------------------------------------*/
/* Ask the system if we are PAL or NTSC and set clock constant accordingly */
/*-------------------------------------------------------------------------*/
if(GfxBase=OpenLibrary("graphics.library",0L))
{
if(((struct GfxBase *)GfxBase)->DisplayFlags & PAL)
clock = PAL_CLOCK;
else
clock = NTSC_CLOCK;
CloseLibrary((struct Library *) GfxBase);
}
printf("OpenAudio: For period calculations, clock=%ld\n", clock);
/* calculate period values for one octave based on system clock */
for(k=0; k<12; k++)
{
period = ((per_ntsc[k] * clock) + (NTSC_CLOCK >> 1)) / NTSC_CLOCK;
per[k] = period;
D(bug("per[%ld]=%ld ",k,per[k]));
}
D(bug("\n"));
/*-------------------------------------------------------------------*/
/* Create a reply port so the audio device can reply to our commands */
/*-------------------------------------------------------------------*/
if(!(port=CreatePort(0,0)))
{ error = 1; goto bailout; }
/*--------------------------------------------------------------------------*/
/* Create audio I/O blocks so we can send commands to the audio device */
/*--------------------------------------------------------------------------*/
for(k=0; k<AIOCNT; k++)
{
if(!(aio[k]=(struct IOAudio *)CreateExtIO(port,sizeof(struct IOAudio))))
{ error = k+2; goto bailout; }
}
/*----------------------------------------------------------------------*/
/* Set up the audio I/O block for channel allocation: */
/* ioa_Request.io_Message.mn_ReplyPort is the address of a reply port. */
/* ioa_Request.io_Message.mn_Node.ln_Pri sets the precedence (priority) */
/* of our use of the audio device. Any tasks asking to use the audio */
/* device that have a higher precedence will steal the channel from us.*/
/* ioa_Request.io_Command is the command field for IO. */
/* ioa_Request.io_Flags is used for the IO flags. */
/* ioa_AllocKey will be filled in by the audio device if the allocation */
/* succeeds. We must use the key it gives for all other commands sent.*/
/* ioa_Data is a pointer to the array listing the channels we want. */
/* ioa_Length tells how long our list of channels is. */
/*----------------------------------------------------------------------*/
aio[0]->ioa_Request.io_Command = ADCMD_ALLOCATE;
aio[0]->ioa_Request.io_Flags = ADIOF_NOWAIT;
aio[0]->ioa_AllocKey = 0;
aio[0]->ioa_Data = whichannel;
aio[0]->ioa_Length = sizeof(whichannel);
/*-----------------------------------------------*/
/* Open the audio device and allocate a channel */
/*-----------------------------------------------*/
if(!(OpenDevice("audio.device",0L, (struct IORequest *) aio[0] ,0L)))
devopened = TRUE;
else { error = 5; goto bailout; }
/* Clone the flags, channel allocation, etc. into other IOAudio requests */
for(k=1; k<AIOCNT; k++) *aio[k] = *aio[0];
bailout:
if(error)
{
printf("OpenAudio errored out at step %ld\n",error);
CloseAudio();
}
return(error);
}
/* CloseAudio
*
* Close audio device as opened by OpenAudio, null out pointers
*/
void CloseAudio()
{
int k;
D(bug("Closing audio device...\n"));
/* Note - we know we have no outstanding audio requests */
if(devopened)
{
CloseDevice((struct IORequest *) aio[0]);
devopened = FALSE;
}
for(k=0; k<AIOCNT; k++)
{
if(aio[k]) DeleteExtIO(aio[k]), aio[k] = NULL;
}
if(port) DeletePort(port), port = NULL;
}
/** PlaySample() **********************************************
*
* Play a note in octave for delay/50ths of a second
* OR Play a sound effect (set octave and note to 0, -1)
*
* Requires successful OpenAudio() called previously
*
* When playing notes:
* Expects note values between 0 (C) and 11 (B#)
* Uses largest octave sample in 8SVX as octave 0, next smallest
* as octave 1, etc.
*
* Notes - this simple example routine does not do ATAK and RLSE)
* - use of Delay for timing is simplistic, synchronous, and does
* not take into account that the oneshot itself may be
* longer than the delay.
* Use timer.device for more accurate asynchronous delays
*
*************************************************************************/
/* Max playable sample in one IO request is 128K */
#define MAXSAMPLE 131072
LONG PlaySample(struct EightSVXInfo *esvx,
LONG octave, LONG note, UWORD volume, ULONG delay)
{
/* pointers to outstanding requests */
struct IOAudio *aout0=NULL, *aout1=NULL;
ULONG period;
LONG osize, rsize;
BYTE *oneshot, *repeat;
if(!devopened) return(-1);
if(note > 11) note=0;
if( note == -1 ) period = clock / esvx->Vhdr.samplesPerSec;
else period = per[note]; /* table set up by OpenAudio */
if(octave > esvx->Vhdr.ctOctave) octave = 0;
if(volume > 64) volume = 64;
oneshot = esvx->osamps[octave];
osize = esvx->osizes[octave];
repeat = esvx->rsamps[octave];
rsize = esvx->rsizes[octave];
D(bug("oneshot $%lx size %ld, repeat $%lx size %ld\n",
oneshot, osize, repeat, rsize));
/*------------------------------------------------------------*/
/* Set up audio I/O blocks to play a sample using CMD_WRITE. */
/* Set up one request for the oneshot and one for repeat */
/* (all ready for simple case, but we may not need both) */
/* The io_Flags are set to ADIOF_PERVOL so we can set the */
/* period (speed) and volume with the our sample; */
/* ioa_Data points to the sample; ioa_Length gives the length */
/* ioa_Cycles tells how many times to repeat the sample */
/* If you want to play the sample at a given sampling rate, */
/* set ioa_Period = clock/(given sampling rate) */
/*------------------------------------------------------------*/
aio[0]->ioa_Request.io_Command =CMD_WRITE;
aio[0]->ioa_Request.io_Flags =ADIOF_PERVOL;
aio[0]->ioa_Data =oneshot;
aio[0]->ioa_Length =osize;
aio[0]->ioa_Period =period;
aio[0]->ioa_Volume =volume;
aio[0]->ioa_Cycles =1;
aio[2]->ioa_Request.io_Command =CMD_WRITE;
aio[2]->ioa_Request.io_Flags =ADIOF_PERVOL;
aio[2]->ioa_Data =repeat;
aio[2]->ioa_Length =rsize;
aio[2]->ioa_Period =period;
aio[2]->ioa_Volume =volume;
aio[2]->ioa_Cycles =0; /* repeat until stopped */
/*---------------------------------------------------*/
/* Send the command to start a sound using BeginIO() */
/* Go to sleep and wait for the sound to finish with */
/* WaitIO() to wait and get the get the ReplyMsg */
/*---------------------------------------------------*/
printf("Starting tone O len %ld for %0ld cyc, R len %ld for %0ld cyc, per=%ld...",
osize, aio[0]->ioa_Cycles, rsize, aio[1]->ioa_Cycles, period);
if(osize)
{
/* Simple case for oneshot sample <= 128K (ie. most samples) */
if(osize <= MAXSAMPLE) BeginIO((struct IORequest *)(aout0=aio[0]));
}
if(rsize)
{
/* Simple case for oneshot sample <= 128K (ie. most samples) */
if(rsize <= MAXSAMPLE) BeginIO((struct IORequest *)(aout1=aio[2]));
}
if(delay) Delay(delay); /* crude timing for notes */
/* Wait for any requests we still have out */
if(aout0) WaitIO(aout0);
if(aout1)
{
if(note >= 0) AbortIO(aout1); /* if a note, stop it now */
WaitIO(aout1);
}
printf("Done\n");
}
/** LoadSample() **********************************************************
*
* Read 8SVX, given an initialized EightSVXInfo with not-in-use IFFHandle,
* and filename. Leaves the IFFHandle open so you can FindProp()
* additional chunks or copychunks(). You must UnloadSample()
* when done. UnloadSample will closeifile if the file is still
* open.
*
* Fills in esvx->Vhdr and Name, and allocates/loads esvx->sample,
* setting esvx->samplebytes to size for deallocation.
*
* Returns 0 for success of an IFFERR (libraries/iffparse.h)
*************************************************************************/
LONG LoadSample(struct EightSVXInfo *esvx, UBYTE *filename)
{
struct IFFHandle *iff;
struct StoredProperty *sp;
Voice8Header *vhdr;
BYTE *oneshot, *repeat;
ULONG osize, rsize, spcyc;
int oct;
LONG error = 0L;
D(bug("LoadSample: looking for %s\n",filename));
if(!esvx) return(CLIENT_ERROR);
if(!(iff=esvx->ParseInfo.iff)) return(CLIENT_ERROR);
if(!(error = openifile((struct ParseInfo *)esvx, filename, IFFF_READ)))
{
printf("Reading '%s'...\n",filename);
error = parseifile((struct ParseInfo *)esvx,
ID_FORM, ID_8SVX,
esvx->ParseInfo.propchks,
esvx->ParseInfo.collectchks,
esvx->ParseInfo.stopchks);
D(bug("LoadSample: after parseifile - error = %ld\n",error));
if((!error)||(error == IFFERR_EOC)||(error == IFFERR_EOF))
{
if(contextis(iff,ID_8SVX,ID_FORM))
{
D(bug("LoadSample: context is 8SVX\n"));
if(!(sp = FindProp(iff,ID_8SVX,ID_VHDR)))
{
message("No 8SVX.VHDR!");
error = IFFERR_SYNTAX;
}
else
{
D(bug("LoadSample: Have VHDR\n"));
/* copy Voice8Header into frame */
vhdr = (Voice8Header *)(sp->sp_Data);
*(&esvx->Vhdr) = *vhdr;
/* copy name if any */
esvx->name[0]='\0';
if(sp = FindProp(iff,ID_8SVX,ID_NAME))
{
strncpy(esvx->name,sp->sp_Data,sp->sp_Size);
esvx->name[MIN(sp->sp_Size,79)] = '\0';
}
error = LoadSBody(esvx);
D(bug("LoadSample: After LoadSBody - error = %ld\n",error));
if(!error)
{
osize = esvx->Vhdr.oneShotHiSamples;
rsize = esvx->Vhdr.repeatHiSamples;
spcyc = esvx->Vhdr.samplesPerHiCycle;
if(!spcyc) spcyc = esvx->Vhdr.repeatHiSamples;
if(!spcyc) spcyc = 8;
oneshot = esvx->sample;
for(oct = esvx->Vhdr.ctOctave-1; oct >= 0;
oct--, oneshot+=(osize+rsize),
osize <<= 1, rsize <<=1, spcyc <<=1)
{
repeat = oneshot + osize;
esvx->osizes[oct] = osize;
if(osize) esvx->osamps[oct] = oneshot;
else esvx->osamps[oct] = 0;
esvx->rsizes[oct] = rsize;
if(rsize) esvx->rsamps[oct] = repeat;
else esvx->rsamps[oct] = 0;
esvx->spcycs[oct] = spcyc;
D(bug("oneshot $%lx size %ld, repeat $%lx size %ld\n",
oneshot, osize, repeat, rsize));
}
}
}
}
else
{
message("Not an 8SVX\n");
error = NOFILE;
}
}
}
if(error)
{
closeifile((struct ParseInfo *)esvx);
UnloadSample(esvx);
}
return(error);
}
/** UnloadSample() *******************************************************
*
* Frees and closes everything opened/alloc'd by LoadSample()
*
*************************************************************************/
void UnloadSample(struct EightSVXInfo *esvx)
{
if(esvx)
{
D(bug("UnloadSample: About to UnloadSBody\n"));
UnloadSBody(esvx);
D(bug("UnloadSample: About to closeifile\n"));
closeifile((struct ParseInfo *)esvx);
}
}
/** LoadSBody() ***********************************************************
*
* Read a 8SVX Sample BODY into RAM.
*
*************************************************************************/
LONG LoadSBody(struct EightSVXInfo *esvx)
{
struct IFFHandle *iff;
LONG sbytes, rlen, error = 0L;
ULONG memtype;
Voice8Header *vhdr = &esvx->Vhdr;
BYTE *t;
D(bug("LoadSBody:\n"));
if(!(iff=esvx->ParseInfo.iff)) return(CLIENT_ERROR);
if(!esvx) return(CLIENT_ERROR);
if(!(currentchunkis(iff,ID_8SVX,ID_BODY)))
{
message("LoadSBody: not at BODY!");
return(IFFERR_READ);
}
sbytes = ChunkMoreBytes(CurrentChunk(iff));
/* if we have to decompress, let's just load it into public mem */
memtype = vhdr->sCompression ? MEMF_PUBLIC : MEMF_CHIP;
D(bug("LoadSBody: samplebytes=%ld, compression=%ld\n",
sbytes,vhdr->sCompression));
if(!(esvx->sample = (BYTE *)AllocMem(sbytes, memtype)))
{
error = CLIENT_ERROR;
}
else
{
D(bug("LoadSBody: have load buffer\n"));
esvx->samplebytes = sbytes;
if(rlen=ReadChunkBytes(iff,esvx->sample,sbytes) != sbytes)
error = IFFERR_READ;
if(error)
{
D(bug("LoadSBody: ReadChunkBytes error = %ld, read %ld bytes\n",
error));
UnloadSample(esvx);
}
else if (vhdr->sCompression) /* Decompress, if needed. */
{
if(t = (BYTE *)AllocMem(sbytes<<1, MEMF_CHIP))
{
D(bug("LoadSBody: have decompression buffer\n"));
DUnpack(esvx->sample, sbytes, t);
FreeMem(esvx->sample, sbytes);
esvx->sample = t;
esvx->samplebytes = sbytes << 1;
}
else
{
UnloadSample(esvx);
error = IFFERR_NOMEM;
}
}
}
return(error);
}
/** UnloadSBody() ********************************************************
*
* Deallocates esvx->smaple
*
*************************************************************************/
void UnloadSBody(struct EightSVXInfo *esvx)
{
if(esvx)
{
D(bug("UnloadSBody:\n"));
if(esvx->sample)
{
D(bug("About to free SBody\n"));
FreeMem(esvx->sample,esvx->samplebytes);
esvx->sample = NULL;
}
esvx->samplebytes = NULL;
}
}
/* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */
/* Fibonacci delta encoding for sound data */
BYTE codeToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};
/* Unpack Fibonacci-delta encoded data from n byte source
* buffer into 2*n byte dest buffer, given initial data
* value x. It returns the lats data value x so you can
* call it several times to incrementally decompress the data.
*/
BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x)
{
BYTE d;
LONG i, lim;
lim = n << 1;
for (i=0; i < lim; ++i)
{
/* Decode a data nibble, high nibble then low nibble */
d = source[i >> 1]; /* get a pair of nibbles */
if (i & 1) /* select low or high nibble */
d &= 0xf; /* mask to get the low nibble */
else
d >>= 4; /* shift to get the high nibble */
x += codeToDelta[d]; /* add in the decoded delta */
dest[i] = x; /* store a 1 byte sample */
}
return(x);
}
/* Unpack Fibonacci-delta encoded data from n byte
* source buffer into 2*(n-2) byte dest buffer.
* Source buffer has a pad byte, an 8-bit initial
* value, followed by n-2 bytes comprising 2*(n-2)
* 4-bit encoded samples.
*/
void DUnpack(source, n, dest)
BYTE source[], dest[];
LONG n;
{
D1Unpack(source+2, n-2, dest, source[1]);
}
/** LoadSMUS() **********************************************************
*
* Read SMUS, given an initialized SMUSInfo with not-in-use IFFHandle,
* and filename. Leaves the IFFHandle open so you can FindProp()
* additional chunks or copychunks(). You must UnloadSMUS()
* when done. UnloadSMUS will closeifile if the file is still
* open.
*
* Fills in smus->Shdr and Name, and sets up smus traks[],
* array setting smus->nevents[] to number of events in each.
*
* Returns 0 for success of an IFFERR (libraries/iffparse.h)
*************************************************************************/
LONG LoadSMUS(struct SMUSInfo *smus, UBYTE *filename)
{
struct ContextNode *cn;
struct IFFHandle *iff;
struct StoredProperty *sp;
SScoreHeader *shdr;
LONG error = 0L, rlen, inslock, oldlock;
UBYTE insname[80];
D(bug("LoadSMUS:\n"));
if(!smus) return(CLIENT_ERROR);
if(!(iff=smus->ParseInfo.iff)) return(CLIENT_ERROR);
if(!(error = openifile((struct ParseInfo *)smus, filename, IFFF_READ)))
{
printf("Reading '%s'...\n",filename);
error = parseifile((struct ParseInfo *)smus,
ID_FORM, ID_SMUS,
smus->ParseInfo.propchks,
smus->ParseInfo.collectchks,
smus->ParseInfo.stopchks);
D(bug("LoadSMUS: after parseifile - error:%ld, %s\n",error,IFFerr(error)));
if((!error)||(error == IFFERR_EOC)||(error == IFFERR_EOF))
{
if(contextis(iff,ID_SMUS,ID_FORM))
{ /* Is a SMUS */
D(bug("LoadSMUS: context is SMUS\n"));
if(!(sp = FindProp(iff,ID_SMUS,ID_SHDR)))
{
message("No SMUS.SHDR!");
error = IFFERR_SYNTAX;
}
else
{
D(bug("LoadSMUS: Have SHDR\n"));
/* copy SScoreHeader into frame */
shdr = (SScoreHeader *)(sp->sp_Data);
*(&smus->Shdr) = *shdr;
/* copy name if any */
smus->name[0]='\0';
if(sp = FindProp(iff,ID_SMUS,ID_NAME))
{
strncpy(smus->name,sp->sp_Data,sp->sp_Size);
smus->name[MIN(sp->sp_Size,79)] = '\0';
}
while(1)
{
/* We only asked to stop at SMUS INS1 and TRAK chunks
* If no error we've hit a stop chunk
*/
cn = CurrentChunk(iff);
if((cn)&&(cn->cn_Type == ID_SMUS)&&
(cn->cn_ID == ID_INS1))
{
printf("INS1 chunk found\n");
if(esvxs[ei] = (struct EightSVXInfo *)
AllocMem(sizeof(struct EightSVXInfo),
MEMF_PUBLIC|MEMF_CLEAR))
{
/* Clone but needs new handle */
*(esvxs[ei]) = *esvx;
if(!(esvxs[ei]->ParseInfo.iff =
AllocIFF())) bye(noiffh,RETURN_FAIL);
/* Get name of sound */
rlen=ReadChunkBytes(iff,&smus->insflags[ei],4);
rlen=ReadChunkBytes(iff,insname,
MIN(cn->cn_Size - 4, 79));
if(rlen != (cn->cn_Size-4)) error = IFFERR_READ;
else insname[rlen] = '\0';
D(bug("Instrument name is %s\n",insname));
if(!error)
{
if(inslock = Lock(inspath,ACCESS_READ))
{
oldlock = CurrentDir(inslock);
error = LoadSample(esvxs[ei],insname);
CurrentDir(oldlock);
UnLock(inslock);
}
if (error) printf("Can't load instrument %s\n",
insname);
}
ei++;
ecnt++;
}
}
else if((cn)&&(cn->cn_Type == ID_SMUS)&&
(cn->cn_ID == ID_TRAK))
{
printf("TRAK chunk found, size=%ld\n",
cn->cn_Size);
if( smus->traks[ti] = (struct SEvent *)
AllocMem(cn->cn_Size, MEMF_PUBLIC))
{
smus->tbytes[ti] = cn->cn_Size;
if((rlen=ReadChunkBytes(iff,smus->traks[ti],
cn->cn_Size)) != cn->cn_Size)
error = IFFERR_READ;
printf(" Loaded at $%lx\n",smus->traks[ti]);
ti++;
tcnt++;
}
else error = IFFERR_NOMEM;
}
if(error) break;
else error = ParseIFF(iff,IFFPARSE_SCAN);
if(error == IFFERR_EOC) continue; /* enter next context */
else if(error) break;
}
}
}
else
{
message("Not a SMUS\n");
error = NOFILE;
}
}
}
if(error == IFFERR_EOC) error = 0;
D(bug("LoadSMUS: error = %ld\n",error));
if(error)
{
closeifile((struct ParseInfo *)smus);
UnloadSMUS(smus);
}
return(error);
}
/** UnloadSMUS() *******************************************************
*
* Frees and closes everything opened/alloc'd by LoadSMUS()
*
*************************************************************************/
void UnloadSMUS(struct SMUSInfo *smus)
{
int ti;
D(bug("In UnloadSMUS\n"));
if(smus)
{
for(ti=0; (ti < MAXTRACKS) && (smus->traks[ti]); ti++)
{
printf("Freeing %ld at $%lx\n",smus->traks[ti],smus->tbytes[ti]);
FreeMem(smus->traks[ti],smus->tbytes[ti]);
smus->traks[ti] = NULL;
}
closeifile((struct ParseInfo *)smus);
}
}